﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.DirectoryServices;
using System.IO;
using System.Management.Automation.Runspaces;
using System.Management.Automation;
using System.Management;
using System.Management.Automation.Host;
using System.Xml;
using System.Threading;
using System.Net;
using System.Text;
using System.Collections.ObjectModel;

namespace Main
{
    public class ControlUsers
    {
        public class Model
        {
            public class User
            {
                private String _name;
                private String _surname;
                private String _phone;
                private String _email;
                private Boolean _isAdmin;
                private int _id;

                public User()
                {

                }
                public int Id
                {
                    get { return _id; }
                    set { _id = value; }
                }
                public String Name
                {
                    get { return _name; }
                    set { _name = value; }
                }
                public String Surname
                {
                    get { return _surname; }
                    set { _surname = value; }
                }
                public String Phone
                {
                    get { return _phone; }
                    set { _phone = value; }
                }
                public String Email
                {
                    get { return _email; }
                    set { _email = value; }
                }
                public Boolean IsAdmin
                {
                    get { return _isAdmin; }
                    set { _isAdmin = value; }
                }
            }
        }


        public class Control
        {
            ControlCommon common = new ControlCommon();

            /// <summary>
            /// lists all available users from SQL Azure
            /// </summary>
            /// <returns></returns>
            public List<User> ViewUsers(string companyName)
            {
                TechnoStoreMainDataContext db = new TechnoStoreMainDataContext();
                var query = from u in db.Users
                            orderby u.name, u.surname ascending
                            where u.name != String.Empty && u.company == companyName
                            select u;

                return query.ToList();
            }

            /// <summary>
            /// Creates user in selected domain on SQL Azure
            /// </summary>
            /// <param name="firstName"></param>
            /// <param name="lastName"></param>
            /// <param name="phone"></param>
            /// <param name="personalEmail"></param>
            /// <param name="selectedDomain"></param>
            /// <returns></returns>
            public String CreateUser(string firstName, string lastName, string personalEmail, string selectedDomain, bool isAdmin, string companyName)
            {
                try
                {
                    string alias = firstName.ToLower() + lastName.ToLower();
                    string displayName = firstName + " " + lastName;
                    string userName = firstName.ToLower() + "." + lastName.ToLower();
                    
                    Boolean pass = true;
                    if (common.IsUserExist(userName))
                    {
                        pass = false;
                    }
                    int counter = 1;
                    if (!pass)
                        counter++;
                    while (!pass)
                    {
                        String newName = "";
                        newName = userName + counter.ToString();
                        if (!common.IsUserExist(newName))
                        {
                            pass = true;
                            userName = newName;
                            displayName += counter.ToString();
                            break;
                        }
                        else
                        {
                            counter++;
                        }

                    }

                    string email = userName + "@" + selectedDomain;
                    string userPrincipalName = userName + "@" + selectedDomain;

                    //for demo purposes given password will be used instead of randomly generated > workaround to the SSO problem with Office 365
                    // string newPassword = "Rty68ou12";

                    string newPassword = ControlCommon.RandomPassword.Generate(8, 10);
                   
                    TechnoStoreMainDataContext db = new TechnoStoreMainDataContext();
                    User user = new User();
                    user.name = firstName;
                    user.surname = lastName + ((counter > 1) ? counter.ToString() : String.Empty);
                    user.username = userName;
                    user.password = ControlCommon.getMd5Hash(newPassword);
                    user.email = email;
                    user.personalemail = personalEmail;
                    user.company = companyName;
                    user.isadmin = isAdmin;
                    user.changepassword = false;
                    db.Users.InsertOnSubmit(user);
                    db.SubmitChanges();

                    var query = from u in db.Users
                                where u.username == user.username
                                select new { u.ID };
                    int id = query.SingleOrDefault().ID;
                    
                    if(id !=-1)
                    {
                        Boolean pass365 = false;
                        var query2 = from u in db.WindowsLiveIDs
                                    where u.WL_ID != String.Empty && u.Company == companyName
                                    select new { u.WL_ID };
                        string exactDomain = query2.SingleOrDefault().WL_ID;
                        exactDomain = exactDomain.Substring(exactDomain.IndexOf('@')+1);
                        if(exactDomain.Contains(".onmicrosoft.com"))
                            exactDomain = exactDomain.Substring(0,exactDomain.IndexOf('.'));

                        if (CreateUserOn365(userPrincipalName, displayName, firstName, lastName, newPassword, exactDomain, companyName))
                            pass365 = true;

                        if(pass365)
                        {
                            string mailBody = "Welcome to TechnoStore! <p />Your username is: <b>" + userPrincipalName + " </b> and your password is: <b>" + newPassword + "</b> <br />Address: https://technostore.cloudapp.net";
                            ControlFunctions.SendEMail(user.personalemail, "Welcome to TechnoStore", mailBody, companyName);
                            return String.Empty;
                        }
                        else
                        {
                              db.Users.DeleteOnSubmit(db.Users.Where(u => u.ID == id).SingleOrDefault());
                              db.SubmitChanges();

                              return "Exchange Online account activation was unseccessfull!";
                        }
                    }

                    else
                        return "SQL Azure record creation was unseccessfull!";
                }
                catch (Exception e)
                {
                    return e.ToString();
                }                 
            }

            /// <summary>
            /// Triggers Powershell to automatically create user on Office 365, enable him and assign a license to him
            /// </summary>
            /// <param name="userPrincipalName"></param>
            /// <param name="displayName"></param>
            /// <param name="firstName"></param>
            /// <param name="lastName"></param>
            /// <param name="newPassword"></param>
            /// <param name="exactDomain"></param>
            /// <returns></returns>
            public bool CreateUserOn365(string userPrincipalName, string displayName, string firstName, string lastName, string newPassword, string exactDomain, string companyName)
            {
                try
                {
                    common.CreateLog("Starting Create of User on 365 for: " + userPrincipalName);

                    TechnoStoreMainDataContext db = new TechnoStoreMainDataContext();
                    var query = from u in db.WindowsLiveIDs
                                where u.WL_ID != String.Empty && u.Company == companyName
                                select new { u.WL_ID };
                    string credentialID = query.SingleOrDefault().WL_ID;

                    var query2 = from u in db.WindowsLiveIDs
                                 where u.WL_ID != String.Empty && u.Company == companyName
                                 select new { u.WL_Password };
                    string credentialPW = query2.SingleOrDefault().WL_Password;

                    string planType = GetO365PlanType(credentialID,credentialPW);
                                     
                    //check the identity, the application is using (useful if there are access denied exceptions)
                    string current = "ID: " + System.Security.Principal.WindowsIdentity.GetCurrent().Name + " - Autenticated? " + System.Security.Principal.WindowsIdentity.GetCurrent().IsAuthenticated + " -System? " + System.Security.Principal.WindowsIdentity.GetCurrent().IsSystem;
                    common.CreateLog("Current identity is: " + current);

                    //New-MsolUser -UserPrincipalName pavel.slavov@technostore1.onmicrosoft.com -DisplayName "Pavel Slavov" -FirstName "Pavel" -LastName "Slavov" -LicenseAssignment "technostore1:ENTERPRISEPACK" -Password $pw -PasswordNeverExpires $true -UsageLocation "US"
                    //                   @"New-MsolUser -UserPrincipalName " + userPrincipalName + @" -DisplayName """ + displayName + @""" -FirstName """ + firstName + @""" -LastName """ + lastName + @""" -LicenseAssignment """ + exactDomain + @":LITEPACK"" -Password """ + newPassword + @""" -PasswordNeverExpires $true -ForceChangePassword $false -UsageLocation ""US""",

                    string[] psScript = { @"$pass = convertto-securestring -asplaintext """ + credentialPW + @""" -force",
                                    @"$credential = new-object -typename System.Management.Automation.PSCredential -argumentlist """ + credentialID + @""", $pass",
                                    @"Import-Module MSOnline",
                                    @"Connect-MsolService -Credential $credential",
                                    @"New-MsolUser -UserPrincipalName " + userPrincipalName + @" -DisplayName """ + displayName + @""" -FirstName """ + firstName + @""" -LastName """ + lastName + @""" -Password """ + newPassword + @""" -PasswordNeverExpires $true -ForceChangePassword $false -UsageLocation ""US""",
                                    @"Set-MsolUserLicense -UserPrincipalName " + userPrincipalName + @" -AddLicenses """ + planType + "\"",
                                    ""};
                    if (File.Exists(common.getPath("scriptNewUser.ps1")))
                        File.Delete(common.getPath("scriptNewUser.ps1"));
                    System.IO.File.WriteAllLines(common.getPath("scriptNewUser.ps1"), psScript);
                    RunspaceConfiguration rsConfig = RunspaceConfiguration.Create();
                    Runspace myRunspace = RunspaceFactory.CreateRunspace(rsConfig);
                    myRunspace.Open();
                    Command command = new Command(common.getPath("scriptNewUser.ps1"));
                    Pipeline pipeline = myRunspace.CreatePipeline();
                    pipeline.Commands.Add(command);                 
                    StringBuilder stringBuilder = new StringBuilder();
                    Collection<PSObject> results = pipeline.Invoke();
                    // check for errors (non-terminating) 
                    if (pipeline.Error.Count > 0)
                    {
                        //iterate over Error PipeLine until end 
                        while (!pipeline.Error.EndOfPipeline)
                        {
                            //read one PSObject off the pipeline 
                            var value = pipeline.Error.Read() as PSObject;
                            if (value != null)
                            {
                                //get the ErrorRecord 
                                var r = value.BaseObject as ErrorRecord;
                                if (r != null)
                                {
                                    //build whatever kind of message your want 
                                    stringBuilder.AppendLine(r.InvocationInfo.MyCommand.Name + " : " + r.Exception.Message);
                                    stringBuilder.AppendLine(r.InvocationInfo.PositionMessage);
                                    stringBuilder.AppendLine(string.Format("+ CategoryInfo: {0}", r.CategoryInfo));
                                    stringBuilder.AppendLine(
                                    string.Format("+ FullyQualifiedErrorId: {0}", r.FullyQualifiedErrorId));
                                }
                            }
                        }
                        common.CreateLog("Stopping Creation of User on 365 for " + userPrincipalName + " with the following ERROR: " + stringBuilder.ToString());
                        pipeline.Dispose();
                        myRunspace.Close();
                        myRunspace.Dispose();
                        rsConfig = null;
                        db.Dispose();
                        //if pipeline has any errors, not exceptions, the user will be created and must be deleted because of the errors
                        //the following script deletes the user from Office 365
                        try
                        {                           
                            common.CreateLog("Starting Deletion of: " + userPrincipalName);
                            string[] script = { @"$pass = convertto-securestring -asplaintext """ + credentialPW + @""" -force",
                                        @"$credential = new-object -typename System.Management.Automation.PSCredential -argumentlist """ + credentialID + @""", $pass",
                                        @"Import-Module MSOnline",
                                        @"Connect-MsolService -Credential $credential",
                                        @"Remove-MsolUser -UserPrincipalName " + userPrincipalName + " -force",
                                        ""};
                            if (File.Exists(common.getPath("scriptDeleteUser.ps1")))
                                File.Delete(common.getPath("scriptDeleteUser.ps1"));
                            System.IO.File.WriteAllLines(common.getPath("scriptDeleteUser.ps1"), script);
                            RunspaceConfiguration config = RunspaceConfiguration.Create();
                            Runspace runspace = RunspaceFactory.CreateRunspace(config);
                            runspace.Open();
                            Command cmd = new Command(common.getPath("scriptDeleteUser.ps1"));
                            Pipeline pipe = runspace.CreatePipeline();
                            pipe.Commands.Add(cmd);
                            pipe.Invoke();
                            pipe.Dispose();
                            runspace.Close();
                            runspace.Dispose();
                            config = null;
                            common.CreateLog("Stopping Successful Deletion of: " + userPrincipalName);
                        }
                        catch (Exception ex)
                        {
                            common.CreateLog(ex.ToString());
                        }

                        return false;
                    } 
                    pipeline.Dispose();
                    myRunspace.Close();
                    myRunspace.Dispose();
                    rsConfig = null;
                    db.Dispose();
                    common.CreateLog("Stopping Successful Creation of User on 365 for: " + userPrincipalName);

                    return true;
                }
                catch (Exception e)
                {
                    common.CreateLog(e.ToString());
                    return false;
                }           
            }

            /// <summary>
            /// gets string of O365 Plan type (Account SKU name)
            /// </summary>
            /// <param name="credentialID"></param>
            /// <param name="credentialPW"></param>
            /// <returns></returns>
            public string GetO365PlanType(string credentialID, string credentialPW)
            {
                try
                {
                    common.CreateLog("Starting getting plan type of : " + credentialID.Substring(credentialID.IndexOf('@') + 1));

                    string[] psScript = { @"$pass = convertto-securestring -asplaintext """ + credentialPW + @""" -force",
                                    @"$credential = new-object -typename System.Management.Automation.PSCredential -argumentlist """ + credentialID + @""", $pass",
                                    @"Import-Module MSOnline",
                                    @"Connect-MsolService -Credential $credential",
                                    @"Get-MsolAccountSku",
                                    ""};

                    if (File.Exists(common.getPath("scriptGetPlan.ps1")))
                        File.Delete(common.getPath("scriptGetPlan.ps1"));
                    System.IO.File.WriteAllLines(common.getPath("scriptGetPlan.ps1"), psScript);
                    RunspaceConfiguration rsConfig = RunspaceConfiguration.Create();
                    Runspace myRunspace = RunspaceFactory.CreateRunspace(rsConfig);
                    myRunspace.Open();
                    Command command = new Command(common.getPath("scriptGetPlan.ps1"));
                    Pipeline pipeline = myRunspace.CreatePipeline();
                    pipeline.Commands.Add(command);
                    StringBuilder stringBuilder = new StringBuilder();
                    Collection<PSObject> results = pipeline.Invoke();
                    // check for errors (non-terminating) 
                    if (pipeline.Error.Count > 0)
                    {
                        //iterate over Error PipeLine until end 
                        while (!pipeline.Error.EndOfPipeline)
                        {
                            //read one PSObject off the pipeline 
                            var value = pipeline.Error.Read() as PSObject;
                            if (value != null)
                            {
                                //get the ErrorRecord 
                                var r = value.BaseObject as ErrorRecord;
                                if (r != null)
                                {
                                    //build whatever kind of message your want 
                                    stringBuilder.AppendLine(r.InvocationInfo.MyCommand.Name + " : " + r.Exception.Message);
                                    stringBuilder.AppendLine(r.InvocationInfo.PositionMessage);
                                    stringBuilder.AppendLine(string.Format("+ CategoryInfo: {0}", r.CategoryInfo));
                                    stringBuilder.AppendLine(
                                    string.Format("+ FullyQualifiedErrorId: {0}", r.FullyQualifiedErrorId));
                                }
                            }
                        }
                        common.CreateLog("Stopping getting plan of: " + credentialID.Substring(credentialID.IndexOf('@') + 1) + " with the following ERROR: " + stringBuilder.ToString());
                        pipeline.Dispose();
                        myRunspace.Close();
                        myRunspace.Dispose();
                        rsConfig = null;
                        return "false";
                    }

                    if (results.Count > 0)
                    {
                        stringBuilder = new StringBuilder();
                        foreach (PSObject obj in results)
                        {
                            foreach (PSPropertyInfo psPropertyInfo in obj.Properties)
                            {
                                //get the value of plan type for the given account sku
                                if (psPropertyInfo.Name == "AccountSkuId")
                                    stringBuilder.AppendLine(psPropertyInfo.Value.ToString().Trim());
                            }
                        }
                        pipeline.Dispose();
                        myRunspace.Close();
                        myRunspace.Dispose();
                        rsConfig = null;

                        string output = stringBuilder.ToString().Trim();

                        common.CreateLog("Stopping getting plan of: " + credentialID.Substring(credentialID.IndexOf('@') + 1) + "    PS output: " + output);

                        return output;
                    }
                    else
                        return "false";
                }

                catch (Exception e)
                {
                    common.CreateLog(e.ToString());
                    return "false";
                }
            }

            /// <summary>
            /// creates new admin user of the application on SQL Azure for the given company
            /// </summary>
            /// <param name="name"></param>
            /// <param name="surname"></param>
            /// <param name="company"></param>
            /// <param name="email"></param>
            /// <param name="username"></param>
            /// <param name="password"></param>
            /// <returns></returns>
            public String RegisterUser(string name, string surname, string company, string email, string password)
            {
                try
                {
                    TechnoStoreMainDataContext db = new TechnoStoreMainDataContext();
                    User user = new User();

                    if (user.name == name && user.surname == surname)
                        return "exist";
                    else
                    {
                        user.name = name;
                        user.surname = surname;
                        user.company = company;
                        user.email = email;
                        user.username = email;
                        user.password = ControlCommon.getMd5Hash(password);
                        user.personalemail = email;
                        user.isadmin = true;
                        user.changepassword = false;
                        db.Users.InsertOnSubmit(user);
                        db.SubmitChanges();

                        return String.Empty;
                    }
                }
                catch (Exception e)
                {
                    return e.ToString();
                }
            }
        }
    }
}       